<html>
<head>
<title>game_core.py</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
.s0 { color: #808080;}
.s1 { color: #a9b7c6;}
.s2 { color: #cc7832;}
.s3 { color: #6897bb;}
.s4 { color: #629755; font-style: italic;}
.s5 { color: #6a8759;}
</style>
</head>
<body bgcolor="#2b2b2b">
<table CELLSPACING=0 CELLPADDING=5 COLS=1 WIDTH="100%" BGCOLOR="#606060" >
<tr><td><center>
<font face="Arial, Helvetica" color="#000000">
game_core.py</font>
</center></td></tr></table>
<pre><span class="s0"># game_core.py (保持不变)</span>
<span class="s2">import </span><span class="s1">json</span>
<span class="s2">import </span><span class="s1">os</span>
<span class="s2">import </span><span class="s1">pygame</span>
<span class="s2">from </span><span class="s1">PIL </span><span class="s2">import </span><span class="s1">Image</span><span class="s2">, </span><span class="s1">ImageTk</span><span class="s2">, </span><span class="s1">ImageEnhance</span>
<span class="s2">from </span><span class="s1">concurrent.futures </span><span class="s2">import </span><span class="s1">ThreadPoolExecutor</span>
<span class="s2">import </span><span class="s1">threading</span>


<span class="s2">class </span><span class="s1">GameCore:</span>
    <span class="s2">def </span><span class="s1">__init__(self</span><span class="s2">, </span><span class="s1">resource_paths</span><span class="s2">, </span><span class="s1">screen_width</span><span class="s2">, </span><span class="s1">screen_height):</span>
        <span class="s1">self.resource_paths = resource_paths</span>
        <span class="s1">self.screen_width = screen_width</span>
        <span class="s1">self.screen_height = screen_height</span>
        <span class="s1">self.audio_executor = ThreadPoolExecutor(max_workers=</span><span class="s3">4</span><span class="s1">)</span>
        <span class="s1">self.init_audio()</span>
        <span class="s1">self.reset_state()</span>

    <span class="s2">def </span><span class="s1">reset_state(self):</span>
        <span class="s4">&quot;&quot;&quot;重置游戏状态&quot;&quot;&quot;</span>
        <span class="s1">self.current_chapter = </span><span class="s3">0</span>
        <span class="s1">self.current_dialogue = </span><span class="s3">0</span>
        <span class="s1">self.game_data = </span><span class="s2">None</span>
        <span class="s1">self.backgrounds = {}</span>
        <span class="s1">self.characters = {}  </span><span class="s0"># {char_id: {normal: img, dark: img}}</span>
        <span class="s1">self.character_sprites = {}  </span><span class="s0"># {char_id: {sprite_name: img}}</span>
        <span class="s1">self.current_bgm = </span><span class="s2">None</span>
        <span class="s1">self.sound_cache = {}</span>
        <span class="s1">self.bgm_volume = </span><span class="s3">0.5</span>
        <span class="s1">self.sfx_volume = </span><span class="s3">0.7</span>
        <span class="s1">self.ui_background_loaded = </span><span class="s2">False</span>
        <span class="s1">self.ui_backgrounds = {}</span>

    <span class="s2">def </span><span class="s1">init_audio(self):</span>
        <span class="s4">&quot;&quot;&quot;初始化音频系统&quot;&quot;&quot;</span>
        <span class="s1">pygame.mixer.init(frequency=</span><span class="s3">44100</span><span class="s2">, </span><span class="s1">size=-</span><span class="s3">16</span><span class="s2">, </span><span class="s1">channels=</span><span class="s3">2</span><span class="s2">, </span><span class="s1">buffer=</span><span class="s3">2048</span><span class="s1">)</span>

    <span class="s2">def </span><span class="s1">load_game_data(self</span><span class="s2">, </span><span class="s1">chapter_file=</span><span class="s5">&quot;chapter1.json&quot;</span><span class="s1">):</span>
        <span class="s4">&quot;&quot;&quot;加载游戏剧本数据&quot;&quot;&quot;</span>
        <span class="s1">path = os.path.join(self.resource_paths[</span><span class="s5">&quot;dialogues&quot;</span><span class="s1">]</span><span class="s2">, </span><span class="s1">chapter_file)</span>
        <span class="s2">try</span><span class="s1">:</span>
            <span class="s2">with </span><span class="s1">open(path</span><span class="s2">, </span><span class="s5">&quot;r&quot;</span><span class="s2">, </span><span class="s1">encoding=</span><span class="s5">&quot;utf-8&quot;</span><span class="s1">) </span><span class="s2">as </span><span class="s1">f:</span>
                <span class="s1">self.game_data = json.load(f)</span>
                <span class="s1">print(</span><span class="s5">f&quot;成功加载剧本: </span><span class="s2">{</span><span class="s1">chapter_file</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
        <span class="s2">except </span><span class="s1">FileNotFoundError:</span>
            <span class="s2">raise </span><span class="s1">RuntimeError(</span><span class="s5">f&quot;剧本文件未找到: </span><span class="s2">{</span><span class="s1">path</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
        <span class="s2">except </span><span class="s1">json.JSONDecodeError </span><span class="s2">as </span><span class="s1">e:</span>
            <span class="s2">raise </span><span class="s1">RuntimeError(</span><span class="s5">f&quot;JSON解析错误: </span><span class="s2">{</span><span class="s1">e</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>

    <span class="s2">def </span><span class="s1">preload_resources(self):</span>
        <span class="s4">&quot;&quot;&quot;预加载所有资源&quot;&quot;&quot;</span>
        <span class="s1">print(</span><span class="s5">&quot;开始预加载资源...&quot;</span><span class="s1">)</span>
        <span class="s1">self.preload_backgrounds()</span>
        <span class="s1">self.preload_characters()</span>
        <span class="s1">self.preload_sounds()</span>
        <span class="s1">self.load_ui_backgrounds()</span>
        <span class="s1">print(</span><span class="s5">&quot;资源预加载完成&quot;</span><span class="s1">)</span>

    <span class="s2">def </span><span class="s1">preload_backgrounds(self):</span>
        <span class="s4">&quot;&quot;&quot;预加载背景图片&quot;&quot;&quot;</span>
        <span class="s1">seen = set()</span>
        <span class="s2">for </span><span class="s1">chapter </span><span class="s2">in </span><span class="s1">self.game_data[</span><span class="s5">&quot;chapters&quot;</span><span class="s1">]:</span>
            <span class="s1">seen.add(chapter[</span><span class="s5">&quot;background&quot;</span><span class="s1">])</span>
            <span class="s2">for </span><span class="s1">dlg </span><span class="s2">in </span><span class="s1">chapter[</span><span class="s5">&quot;dialogues&quot;</span><span class="s1">]:</span>
                <span class="s2">if </span><span class="s5">&quot;background&quot; </span><span class="s2">in </span><span class="s1">dlg:</span>
                    <span class="s1">seen.add(dlg[</span><span class="s5">&quot;background&quot;</span><span class="s1">])</span>
        <span class="s2">for </span><span class="s1">bg </span><span class="s2">in </span><span class="s1">seen:</span>
            <span class="s1">self.load_background(bg)</span>

    <span class="s2">def </span><span class="s1">load_background(self</span><span class="s2">, </span><span class="s1">filename):</span>
        <span class="s4">&quot;&quot;&quot;加载单个背景图&quot;&quot;&quot;</span>
        <span class="s1">path = os.path.join(self.resource_paths[</span><span class="s5">&quot;images&quot;</span><span class="s1">]</span><span class="s2">, </span><span class="s1">filename)</span>
        <span class="s2">try</span><span class="s1">:</span>
            <span class="s1">img = Image.open(path)</span>
            <span class="s1">self.backgrounds[filename] = ImageTk.PhotoImage(</span>
                <span class="s1">img.resize((self.screen_width</span><span class="s2">, </span><span class="s1">self.screen_height)</span><span class="s2">, </span><span class="s1">Image.LANCZOS)</span>
            <span class="s1">)</span>
            <span class="s1">print(</span><span class="s5">f&quot;加载背景: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
        <span class="s2">except </span><span class="s1">Exception </span><span class="s2">as </span><span class="s1">e:</span>
            <span class="s1">print(</span><span class="s5">f&quot;加载背景失败: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">, 错误: </span><span class="s2">{</span><span class="s1">e</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s0"># 创建占位背景</span>
            <span class="s1">placeholder = Image.new(</span><span class="s5">'RGB'</span><span class="s2">, </span><span class="s1">(self.screen_width</span><span class="s2">, </span><span class="s1">self.screen_height)</span><span class="s2">, </span><span class="s1">(</span><span class="s3">50</span><span class="s2">, </span><span class="s3">50</span><span class="s2">, </span><span class="s3">100</span><span class="s1">))</span>
            <span class="s1">self.backgrounds[filename] = ImageTk.PhotoImage(placeholder)</span>

    <span class="s2">def </span><span class="s1">preload_characters(self):</span>
        <span class="s4">&quot;&quot;&quot;预加载角色立绘&quot;&quot;&quot;</span>
        <span class="s2">for </span><span class="s1">char_id</span><span class="s2">, </span><span class="s1">data </span><span class="s2">in </span><span class="s1">self.game_data[</span><span class="s5">&quot;characters&quot;</span><span class="s1">].items():</span>
            <span class="s1">self.load_character(char_id</span><span class="s2">, </span><span class="s1">data[</span><span class="s5">&quot;image&quot;</span><span class="s1">])</span>

    <span class="s2">def </span><span class="s1">load_character(self</span><span class="s2">, </span><span class="s1">char_id</span><span class="s2">, </span><span class="s1">filename</span><span class="s2">, </span><span class="s1">is_sprite=</span><span class="s2">False</span><span class="s1">):</span>
        <span class="s4">&quot;&quot;&quot;加载角色立绘&quot;&quot;&quot;</span>
        <span class="s1">path = os.path.join(self.resource_paths[</span><span class="s5">&quot;images&quot;</span><span class="s1">]</span><span class="s2">, </span><span class="s1">filename)</span>
        <span class="s2">try</span><span class="s1">:</span>
            <span class="s1">print(</span><span class="s5">f&quot;尝试加载角色立绘: </span><span class="s2">{</span><span class="s1">char_id</span><span class="s2">} </span><span class="s5">- </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s1">img = Image.open(path).convert(</span><span class="s5">&quot;RGBA&quot;</span><span class="s1">)</span>
            <span class="s1">size = (int(self.screen_width * </span><span class="s3">0.2</span><span class="s1">)</span><span class="s2">, </span><span class="s1">int(self.screen_height * </span><span class="s3">0.5</span><span class="s1">))</span>

            <span class="s0"># 生成正常状态</span>
            <span class="s1">normal = img.resize(size</span><span class="s2">, </span><span class="s1">Image.LANCZOS)</span>
            <span class="s1">alpha = normal.split()[</span><span class="s3">3</span><span class="s1">].point(</span><span class="s2">lambda </span><span class="s1">a: int(a * </span><span class="s3">0.8</span><span class="s1">))</span>
            <span class="s1">normal.putalpha(alpha)</span>

            <span class="s0"># 存储立绘</span>
            <span class="s2">if not </span><span class="s1">is_sprite:</span>
                <span class="s1">self.characters[char_id] = {</span>
                    <span class="s5">&quot;normal&quot;</span><span class="s1">: ImageTk.PhotoImage(normal)</span><span class="s2">,</span>
                    <span class="s5">&quot;dark&quot;</span><span class="s1">: </span><span class="s2">None  </span><span class="s0"># 简化，暂时不需要暗色状态</span>
                <span class="s1">}</span>
                <span class="s1">print(</span><span class="s5">f&quot;成功加载角色立绘: </span><span class="s2">{</span><span class="s1">char_id</span><span class="s2">} </span><span class="s5">- </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s2">else</span><span class="s1">:</span>
                <span class="s2">if </span><span class="s1">char_id </span><span class="s2">not in </span><span class="s1">self.character_sprites:</span>
                    <span class="s1">self.character_sprites[char_id] = {}</span>
                <span class="s1">self.character_sprites[char_id][filename] = ImageTk.PhotoImage(normal)</span>
                <span class="s1">print(</span><span class="s5">f&quot;成功加载角色特殊立绘: </span><span class="s2">{</span><span class="s1">char_id</span><span class="s2">} </span><span class="s5">- </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>

        <span class="s2">except </span><span class="s1">Exception </span><span class="s2">as </span><span class="s1">e:</span>
            <span class="s1">print(</span><span class="s5">f&quot;加载角色立绘失败: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">, 错误: </span><span class="s2">{</span><span class="s1">e</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s0"># 为缺失的立绘提供占位符</span>
            <span class="s1">size = (int(self.screen_width * </span><span class="s3">0.2</span><span class="s1">)</span><span class="s2">, </span><span class="s1">int(self.screen_height * </span><span class="s3">0.5</span><span class="s1">))</span>
            <span class="s1">placeholder = Image.new(</span><span class="s5">'RGBA'</span><span class="s2">, </span><span class="s1">size</span><span class="s2">, </span><span class="s1">(</span><span class="s3">255</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s3">128</span><span class="s1">))</span>

            <span class="s0"># 添加缺失的立绘文字提示</span>
            <span class="s2">from </span><span class="s1">PIL </span><span class="s2">import </span><span class="s1">ImageDraw</span><span class="s2">, </span><span class="s1">ImageFont</span>
            <span class="s1">draw = ImageDraw.Draw(placeholder)</span>
            <span class="s2">try</span><span class="s1">:</span>
                <span class="s0"># 尝试加载系统字体</span>
                <span class="s1">font = ImageFont.truetype(</span><span class="s5">&quot;simhei.ttf&quot;</span><span class="s2">, </span><span class="s3">24</span><span class="s1">)</span>
            <span class="s2">except</span><span class="s1">:</span>
                <span class="s0"># 使用默认字体</span>
                <span class="s1">font = ImageFont.load_default()</span>

            <span class="s1">draw.text((</span><span class="s3">10</span><span class="s2">, </span><span class="s3">10</span><span class="s1">)</span><span class="s2">, </span><span class="s5">f&quot;缺失立绘: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s2">, </span><span class="s1">font=font</span><span class="s2">, </span><span class="s1">fill=(</span><span class="s3">255</span><span class="s2">, </span><span class="s3">255</span><span class="s2">, </span><span class="s3">255</span><span class="s1">))</span>

            <span class="s1">placeholder_img = ImageTk.PhotoImage(placeholder)</span>

            <span class="s2">if not </span><span class="s1">is_sprite:</span>
                <span class="s1">self.characters[char_id] = {</span>
                    <span class="s5">&quot;normal&quot;</span><span class="s1">: placeholder_img</span><span class="s2">,</span>
                    <span class="s5">&quot;dark&quot;</span><span class="s1">: </span><span class="s2">None</span>
                <span class="s1">}</span>
            <span class="s2">else</span><span class="s1">:</span>
                <span class="s2">if </span><span class="s1">char_id </span><span class="s2">not in </span><span class="s1">self.character_sprites:</span>
                    <span class="s1">self.character_sprites[char_id] = {}</span>
                <span class="s1">self.character_sprites[char_id][filename] = placeholder_img</span>

    <span class="s2">def </span><span class="s1">preload_sounds(self):</span>
        <span class="s4">&quot;&quot;&quot;预加载音频资源&quot;&quot;&quot;</span>
        <span class="s1">seen = set()</span>
        <span class="s2">for </span><span class="s1">chapter </span><span class="s2">in </span><span class="s1">self.game_data[</span><span class="s5">&quot;chapters&quot;</span><span class="s1">]:</span>
            <span class="s2">if </span><span class="s5">&quot;music&quot; </span><span class="s2">in </span><span class="s1">chapter:</span>
                <span class="s1">seen.add(chapter[</span><span class="s5">&quot;music&quot;</span><span class="s1">])</span>
            <span class="s2">for </span><span class="s1">dlg </span><span class="s2">in </span><span class="s1">chapter[</span><span class="s5">&quot;dialogues&quot;</span><span class="s1">]:</span>
                <span class="s2">if </span><span class="s5">&quot;sound&quot; </span><span class="s2">in </span><span class="s1">dlg:</span>
                    <span class="s1">seen.add(dlg[</span><span class="s5">&quot;sound&quot;</span><span class="s1">])</span>
        <span class="s2">for </span><span class="s1">sound </span><span class="s2">in </span><span class="s1">seen:</span>
            <span class="s2">if </span><span class="s1">sound:  </span><span class="s0"># 确保不为空</span>
                <span class="s1">self.load_sound(sound)</span>

    <span class="s2">def </span><span class="s1">load_sound(self</span><span class="s2">, </span><span class="s1">filename</span><span class="s2">, </span><span class="s1">bgm=</span><span class="s2">False</span><span class="s1">):</span>
        <span class="s4">&quot;&quot;&quot;加载音频到缓存&quot;&quot;&quot;</span>
        <span class="s2">if </span><span class="s1">filename </span><span class="s2">in </span><span class="s1">self.sound_cache </span><span class="s2">or not </span><span class="s1">filename:</span>
            <span class="s2">return</span>
        <span class="s1">path = os.path.join(self.resource_paths[</span><span class="s5">&quot;sounds&quot;</span><span class="s1">]</span><span class="s2">, </span><span class="s1">filename)</span>
        <span class="s2">try</span><span class="s1">:</span>
            <span class="s2">if </span><span class="s1">bgm:</span>
                <span class="s1">self.sound_cache[filename] = path</span>
            <span class="s2">else</span><span class="s1">:</span>
                <span class="s1">sound = pygame.mixer.Sound(path)</span>
                <span class="s1">sound.set_volume(self.sfx_volume)</span>
                <span class="s1">self.sound_cache[filename] = sound</span>
            <span class="s1">print(</span><span class="s5">f&quot;加载音效: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
        <span class="s2">except </span><span class="s1">Exception </span><span class="s2">as </span><span class="s1">e:</span>
            <span class="s1">print(</span><span class="s5">f&quot;加载音效失败: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">, 错误: </span><span class="s2">{</span><span class="s1">e</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s1">self.sound_cache[filename] = </span><span class="s2">None</span>

    <span class="s2">def </span><span class="s1">play_sound(self</span><span class="s2">, </span><span class="s1">filename</span><span class="s2">, </span><span class="s1">loops=</span><span class="s3">0</span><span class="s1">):</span>
        <span class="s4">&quot;&quot;&quot;播放音效&quot;&quot;&quot;</span>

        <span class="s2">def </span><span class="s1">_play():</span>
            <span class="s1">sound = self.sound_cache.get(filename)</span>
            <span class="s2">if </span><span class="s1">sound </span><span class="s2">and </span><span class="s1">isinstance(sound</span><span class="s2">, </span><span class="s1">pygame.mixer.Sound):</span>
                <span class="s1">sound.play(loops=loops)</span>

        <span class="s1">self.audio_executor.submit(_play)</span>

    <span class="s2">def </span><span class="s1">play_bgm(self</span><span class="s2">, </span><span class="s1">filename):</span>
        <span class="s4">&quot;&quot;&quot;播放背景音乐&quot;&quot;&quot;</span>

        <span class="s2">def </span><span class="s1">_play():</span>
            <span class="s2">if </span><span class="s1">self.current_bgm == filename:</span>
                <span class="s2">return</span>
            <span class="s1">path = self.sound_cache.get(filename)</span>
            <span class="s2">if </span><span class="s1">path:</span>
                <span class="s1">pygame.mixer.music.load(path)</span>
                <span class="s1">pygame.mixer.music.set_volume(self.bgm_volume)</span>
                <span class="s1">pygame.mixer.music.play(-</span><span class="s3">1</span><span class="s1">)</span>
                <span class="s1">self.current_bgm = filename</span>
                <span class="s1">print(</span><span class="s5">f&quot;播放背景音乐: </span><span class="s2">{</span><span class="s1">filename</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s2">else</span><span class="s1">:</span>
                <span class="s1">pygame.mixer.music.stop()  </span><span class="s0"># 停止当前音乐</span>
                <span class="s1">self.current_bgm = </span><span class="s2">None</span>

        <span class="s1">self.audio_executor.submit(_play)</span>

    <span class="s2">def </span><span class="s1">load_ui_backgrounds(self):</span>
        <span class="s4">&quot;&quot;&quot;加载UI背景图片&quot;&quot;&quot;</span>
        <span class="s2">try</span><span class="s1">:</span>
            <span class="s1">ui_bg_path = os.path.join(self.resource_paths[</span><span class="s5">&quot;images&quot;</span><span class="s1">]</span><span class="s2">, </span><span class="s5">&quot;云墨青山.jpg&quot;</span><span class="s1">)</span>
            <span class="s1">ui_bg = Image.open(ui_bg_path)</span>

            <span class="s0"># 创建对话框背景</span>
            <span class="s1">dialogue_box_height = int(self.screen_height * </span><span class="s3">0.2</span><span class="s1">)</span>
            <span class="s1">dialogue_box_width = int(self.screen_width * </span><span class="s3">0.9</span><span class="s1">)</span>
            <span class="s1">crop_y_start = max(</span><span class="s3">0</span><span class="s2">, </span><span class="s1">ui_bg.height - dialogue_box_height * </span><span class="s3">3</span><span class="s1">)</span>
            <span class="s1">dialogue_crop = ui_bg.crop((</span><span class="s3">0</span><span class="s2">, </span><span class="s1">crop_y_start</span><span class="s2">, </span><span class="s1">ui_bg.width</span><span class="s2">, </span><span class="s1">crop_y_start + dialogue_box_height))</span>
            <span class="s1">dialogue_resized = dialogue_crop.resize((dialogue_box_width</span><span class="s2">, </span><span class="s1">dialogue_box_height)</span><span class="s2">, </span><span class="s1">Image.LANCZOS)</span>

            <span class="s0"># 添加半透明遮罩</span>
            <span class="s1">dialogue_mask = Image.new(</span><span class="s5">'RGBA'</span><span class="s2">, </span><span class="s1">dialogue_resized.size</span><span class="s2">, </span><span class="s1">(</span><span class="s3">0</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s3">128</span><span class="s1">))</span>
            <span class="s1">dialogue_with_mask = Image.alpha_composite(dialogue_resized.convert(</span><span class="s5">'RGBA'</span><span class="s1">)</span><span class="s2">, </span><span class="s1">dialogue_mask)</span>
            <span class="s1">self.ui_backgrounds[</span><span class="s5">&quot;dialogue_box&quot;</span><span class="s1">] = ImageTk.PhotoImage(dialogue_with_mask)</span>

            <span class="s0"># 创建按钮背景</span>
            <span class="s1">button_width = </span><span class="s3">300</span>
            <span class="s1">button_height = </span><span class="s3">80</span>
            <span class="s1">button_crop = ui_bg.crop((</span><span class="s3">0</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s1">min(ui_bg.width</span><span class="s2">, </span><span class="s1">button_width)</span><span class="s2">, </span><span class="s1">min(ui_bg.height</span><span class="s2">, </span><span class="s1">button_height)))</span>
            <span class="s1">button_resized = button_crop.resize((button_width</span><span class="s2">, </span><span class="s1">button_height)</span><span class="s2">, </span><span class="s1">Image.LANCZOS)</span>

            <span class="s0"># 正常状态和悬停状态</span>
            <span class="s1">button_mask = Image.new(</span><span class="s5">'RGBA'</span><span class="s2">, </span><span class="s1">button_resized.size</span><span class="s2">, </span><span class="s1">(</span><span class="s3">0</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s3">0</span><span class="s2">, </span><span class="s3">128</span><span class="s1">))</span>
            <span class="s1">button_with_mask = Image.alpha_composite(button_resized.convert(</span><span class="s5">'RGBA'</span><span class="s1">)</span><span class="s2">, </span><span class="s1">button_mask)</span>
            <span class="s1">self.ui_backgrounds[</span><span class="s5">&quot;button_normal&quot;</span><span class="s1">] = ImageTk.PhotoImage(button_with_mask)</span>

            <span class="s1">button_hover = button_with_mask.copy()</span>
            <span class="s1">hover_mask = Image.new(</span><span class="s5">'RGBA'</span><span class="s2">, </span><span class="s1">button_resized.size</span><span class="s2">, </span><span class="s1">(</span><span class="s3">255</span><span class="s2">, </span><span class="s3">255</span><span class="s2">, </span><span class="s3">255</span><span class="s2">, </span><span class="s3">30</span><span class="s1">))</span>
            <span class="s1">button_hover = Image.alpha_composite(button_hover</span><span class="s2">, </span><span class="s1">hover_mask)</span>
            <span class="s1">self.ui_backgrounds[</span><span class="s5">&quot;button_hover&quot;</span><span class="s1">] = ImageTk.PhotoImage(button_hover)</span>

            <span class="s1">self.ui_background_loaded = </span><span class="s2">True</span>
            <span class="s1">print(</span><span class="s5">&quot;UI背景加载成功&quot;</span><span class="s1">)</span>

        <span class="s2">except </span><span class="s1">Exception </span><span class="s2">as </span><span class="s1">e:</span>
            <span class="s1">print(</span><span class="s5">f&quot;UI背景加载失败: </span><span class="s2">{</span><span class="s1">e</span><span class="s2">}</span><span class="s5">&quot;</span><span class="s1">)</span>
            <span class="s1">self.ui_background_loaded = </span><span class="s2">False</span>

    <span class="s2">def </span><span class="s1">cleanup(self):</span>
        <span class="s4">&quot;&quot;&quot;清理资源&quot;&quot;&quot;</span>
        <span class="s1">self.audio_executor.shutdown()</span>
        <span class="s1">pygame.mixer.quit()</span></pre>
</body>
</html>